package top.gytf.unifiedcall.common.support;

import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.core.annotation.AnnotationUtils;
import top.gytf.unifiedcall.common.contract.AbstractApiContractReader;
import top.gytf.unifiedcall.common.detial.UnifiedCallPackageManager;
import top.gytf.unifiedcall.model.contract.ApiContract;
import top.gytf.unifiedcall.model.detail.Version;

import java.lang.reflect.Method;
import java.util.*;
import java.util.function.*;

import static java.util.Comparator.comparing;
import static java.util.function.BinaryOperator.maxBy;
import static java.util.function.BinaryOperator.minBy;

/**
 * 内置的API协定读取逻辑
 *
 * @see UnifiedCallApiContract
 */
@RequiredArgsConstructor
public class UnifiedCallApiContractReader extends AbstractApiContractReader {
    private final UnifiedCallPackageManager packageManager;

    @Override
    public boolean isSupported(Method apiMethod) {
        return CollUtil.isNotEmpty(findAnnotations(apiMethod));
    }

    @Override
    public List<ApiContract> doRead(Method apiMethod) {
        List<UnifiedCallApi> unifiedCallApis = findAnnotations(apiMethod);
        UnifiedCallApiContract.UnifiedCallApiContractBuilder builder = UnifiedCallApiContract.builder();
        Map<String, Object> params = new HashMap<>();

        unifiedCallApis.forEach(unifiedCallApi -> {
            // 是否为自定义响应
            determineParam(
                    firstSupplier(unifiedCallApi.customizedResponse()),
                    acceptParam(UnifiedCallApiContract.Fields.customizedResponse, params, (o, n) -> o, builder::customizedResponse));
            // 支持的最小版本
            determineParam(
                    firstSupplier(unifiedCallApi.since()),
                    acceptParam(UnifiedCallApiContract.Fields.since, params, maxBy(comparing(Version::getValue)), builder::since),
                    UnifiedCallApiContractReader::convert);
            // 支持的最大版本
            determineParam(
                    firstSupplier(unifiedCallApi.until()),
                    acceptParam(UnifiedCallApiContract.Fields.until, params, minBy(comparing(Version::getValue)), builder::until),
                    UnifiedCallApiContractReader::convert);
        });

        return Collections.singletonList(builder.build());
    }

    /**
     * 查找可用于指定方法的UnifiedCallApi注解
     */
    protected List<UnifiedCallApi> findAnnotations(Method apiMethod) {
        List<UnifiedCallApi> annotations = new ArrayList<>(2);

        // 直接声明在API方法上的注解
        UnifiedCallApi onMethod = AnnotationUtils.findAnnotation(apiMethod, UnifiedCallApi.class);
        if (onMethod != null) {
            annotations.add(onMethod);
        }

        // 声明在API客户端类上的注解
        UnifiedCallApi onClass = AnnotationUtils.findAnnotation(apiMethod.getDeclaringClass(), UnifiedCallApi.class);
        if (onClass != null) {
            annotations.add(onClass);
        }

        // 声明在API详情包上的注解
        packageManager.findByApiMethod(apiMethod)
                .map(Object::getClass)
                .map(detail -> AnnotationUtils.findAnnotation(detail, UnifiedCallApi.class))
                .ifPresent(annotations::add);

        return annotations;
    }

    /**
     * 设置参数
     */
    private static <T> Consumer<T> acceptParam(String name, Map<String, Object> params, BinaryOperator<T> combiner, Consumer<T> builderSetter) {
        return v -> {
            Object old = params.get(name);
            if (v.getClass().isInstance(old)) {
                //noinspection unchecked
                v = combiner.apply((T) old, v);
            }
            params.put(name, v);
            builderSetter.accept(v);
        };
    }

    /**
     * 确定协定的参数
     */
    private static <T> void determineParam(Supplier<T> supplier, Consumer<T> consumer) {
        determineParam(supplier, consumer, Function.identity());
    }
    private static <T, R> void determineParam(Supplier<T> supplier, Consumer<R> consumer, Function<T, R> converter) {
        Optional.ofNullable(supplier.get())
                .map(converter)
                .ifPresent(consumer);
    }

    private static Version convert(UnifiedCallApi.Version versionAnno) {
        String value = firstSupplier(versionAnno.value()).get();
        if (value != null) {
            return Version.valueOf(value);
        }
        Integer major = firstSupplier(versionAnno.major()).get();
        Integer minor = firstSupplier(versionAnno.minor()).get();
        Integer patch = firstSupplier(versionAnno.patch()).get();
        if (major != null && minor != null && patch != null) {
            return Version.valueOf(major, minor, patch);
        }
        throw new IllegalArgumentException("无法解析版本号：" + versionAnno);
    }

    @SuppressWarnings("unused")
    private static <T> Supplier<T> firstSupplier(T[] values) {
        return values.length == 0 ? () -> null : () -> values[0];
    }
    private static Supplier<Boolean> firstSupplier(boolean[] values) {
        return values.length == 0 ? () -> null : () -> values[0];
    }
    private static Supplier<Integer> firstSupplier(int[] values) {
        return values.length == 0 ? () -> null : () -> values[0];
    }
}
